from pychecktype.checked import checked

from db.dao import AccountDao, PositionDao
from db.models import StockAccount
from quantification.data_handler.DataHandler import DataHandler
from quantification.data_handler.PositionRecord import PositionRecord
from utils.CommonEnum import DealDirectionEnum
from utils.util import ObjDictTool


class Account(DataHandler):
    '''
    账户资金
    '''
    __slots__ = (
        'initial_capital', 'total_funds', 'total_market_value', 'total_available_funds', 'remaining_available_funds',
        'position_ratio')

    def __init__(self):
        self.__id = None
        # 初始资金
        self.initial_capital = None
        # 资金池仓位比重，用来控制总可用资金上限，根据大盘行情和决策失败率来决定仓位比重大小。熊市时，连续决策失败时降低该比重趋向0，反之趋向于1
        self.position_ratio = 1
        # 总资金=总可用资金+冻结资金
        self.total_funds = None
        # 持股总市值=sum(股价*持股数量)
        self.total_market_value = 0.00
        # 总可用资金=总资金*仓位比
        self.total_available_funds = None
        # 剩余可用资金(=买卖股票时的股价*买卖股数进行加减=    (只能)总可用资金-持股价值)
        self.remaining_available_funds = None

        # 冻结资金=总资金*(1-仓位比)    清仓后进行购买之前进行计算,其他时间是静态数据
        self.freeze_funds = None

        self.update_time = None

        self.load_data()

    def load_data(self):
        stock_account = AccountDao().queryOne()
        if stock_account:
            self.__id = getattr(stock_account, 'id')
            self.initial_capital = float(getattr(stock_account, 'initial_capital'))
            self.total_funds = float(getattr(stock_account, 'total_funds'))
            self.total_market_value = float(getattr(stock_account, 'total_market_value'))
            self.total_available_funds = float(getattr(stock_account, 'total_available_funds'))
            self.remaining_available_funds = float(getattr(stock_account, 'remaining_available_funds'))
            self.position_ratio = getattr(stock_account, 'position_ratio')
            self.freeze_funds = float(getattr(stock_account, 'freeze_funds'))
            self.update_time = getattr(stock_account, 'update_time')

    def store_data(self):
        stock_account = StockAccount()
        ObjDictTool.copyA2B(self, stock_account)
        setattr(stock_account, 'id', self.__id)
        AccountDao().save(stock_account)

    def calculate_account_funds(self, positionRecord: PositionRecord):
        '''
        重新计算账户余额
        :return:
        '''
        if DealDirectionEnum.SELL.code == positionRecord.deal_direction:
            # 卖出,持有股票市值需要减少,总可用资金,剩余可用资金,总资金均需重新计算
            # 交易金额
            deal_funds = positionRecord.price * positionRecord.amount
            # 剩余可用资金=历史剩余可用资金+卖掉的股票市值
            self.remaining_available_funds = self.remaining_available_funds + deal_funds
            # 最新的持股市值
            new_funds = self.calculate_total_market_value()

            # 总市值=当前持股市值+剩余可用资金
            self.total_available_funds = self.remaining_available_funds + new_funds
            self.total_funds = self.total_available_funds + self.freeze_funds
            self.total_market_value = self.total_market_value - deal_funds

        elif DealDirectionEnum.BUY.code == positionRecord.deal_direction:

            # 买入,持有股票市值需要增加,减少总可用资金,剩余可用资金
            # 交易金额
            deal_funds = positionRecord.price * positionRecord.amount

            self.total_market_value = self.total_market_value + deal_funds
            # 剩余可用资金=历史剩余可用资金+卖掉的股票市值
            self.remaining_available_funds = self.remaining_available_funds - deal_funds
            # 最新的持股市值
            new_funds = self.calculate_total_market_value()

            # 总市值=当前持股市值+剩余可用资金
            self.total_available_funds = self.remaining_available_funds + new_funds
            self.total_funds = self.total_available_funds + self.freeze_funds

    def calculate_total_market_value(self):
        '''
        计算持股价值
        '''
        sum_maket_value = 0.00
        stock_position_list = PositionDao().findAll()
        if stock_position_list:
            for position in stock_position_list:
                average_price = float(getattr(position, 'average_price'))
                hold_quantity = getattr(position, 'hold_quantity')
                sum_maket_value = sum_maket_value + average_price * hold_quantity
        return sum_maket_value

    def liquidation_funds(self):
        '''
        清算账户里的总可用资金和冻结资金(当前空仓时计算)
        '''
        market_value = self.calculate_total_market_value()
        if market_value == 0.00:
            # 空仓
            self.total_market_value = market_value
            self.remaining_available_funds = self.total_available_funds = self.total_funds * self.position_ratio
            self.freeze_funds = self.total_funds - self.total_available_funds
        else:
            # 非空仓,需要计算持股市值,总调用资金,总资金
            old_market_value = self.total_market_value
            self.total_market_value = market_value
            # 求差值
            diff_value = market_value - old_market_value
            self.total_available_funds = self.total_available_funds + diff_value
            self.total_funds = self.freeze_funds + self.total_available_funds

    @checked
    def set_initial_capital(self, initial_capital: int):
        self.initial_capital = initial_capital
